/**
 * @see https://cn.mobx.js.org/#mobx-4-vs-mobx-5
 * MobX 4 vs MobX 5
 * MobX 4 和 MobX 5 的不同之处在于后者使用了 ES6 的 proxy 来追踪属性。因此，MobX 5 只能运行在支持 proxy 的浏览器上，而 MobX 4可以运行在任何支持 ES5 的环境中。
 * MobX 4 的重要局限性:
 * Observable 数组并非真正的数组，所以它们无法通过 Array.isArray() 的检查。最常见的处理方法是在传递给第三方库之前，你经常需要先对其进行 .slice() 操作，从而得到一个浅拷贝的真正数组。
 * 向一个已存在的 observable 对象中添加属性不会被自动捕获。要么使用 observable 映射来替代，要么使用[工具函数] 中方法来对想要动态添加属性的对象进行读/写/迭代。
 * @see https://cn.mobx.js.org/refguide/object-api.html [工具函数]
 */

import * as mobx4 from './mobx@4'
import * as mobx5 from './mobx@5'

const _mobx = (typeof Proxy === "undefined" || typeof Symbol === "undefined") ? mobx4 : mobx5;
const autorun = _mobx.autorun;
const reaction = _mobx.reaction;
const when = _mobx.when;
const action = _mobx.action;
const comparer = _mobx.comparer;
const runInAction = _mobx.runInAction;
const configure = _mobx.configure;
const computed = function (...args): any {
    const [arg1, arg2, arg3] = args;
    return _mobx.computed(arg1, arg2, arg3);
};
const observable = function (...args): any {
    const [arg1, arg2, arg3] = args;
    return _mobx.observable(arg1, arg2, arg3);
};

/**
 * 配置 - 必须通过action修改值
 * enforceActions 也被称为“严格模式”。
 * 在严格模式下，不允许在 action 外更改任何状态。 可接收的值:
 * "never" (默认): 可以在任意地方修改状态
 * "observed": 在某处观察到的所有状态都需要通过动作进行更改。在正式应用中推荐此严格模式。
 * "always": 状态始终需要通过动作来更新(实际上还包括创建)。
 */
configure({ enforceActions: 'observed' });

/************************************************MobX工具函数************************************************/
/**
 * 现在有一个统一的工具 API 可以操控 observable 映射、对象和数组。
 * 这些 API 都是响应式的，这意味着如果使用 set 进行添加，使用 values 或 keys 进行迭代，即便是新属性的声明都可以被 MobX 检测到。
 */
/**
 * 将集合中的所有值作为数组返回
 * @example
 * values(thing)
 */
const values = _mobx.values;
/**
 * 将集合中的所有键作为数组返回
 * @example
 * keys(thing)
 */
const keys = _mobx.keys;
/**
 * 返回集合中的所有项的键值对数组
 * @example
 * entries(thing)
 */
const entries = _mobx.entries;

/**
 * [由于MobX4的局限性可能需要用到以下方法]
 */
/**
 * 使用提供的键值对来更新给定的集合
 * @see https://cn.mobx.js.org/refguide/object-api.html
 * @example 
 * set(thing, key, value)
 * set(thing, { key: value })
 */
const set = _mobx.set;
/**
 * 返回指定键下的子项
 * @see https://cn.mobx.js.org/refguide/object-api.html
 * @example
 * get(thing, key)
 */
const get = _mobx.get;
/**
 * 从集合中移除指定的项。用于数组拼接
 * @see https://cn.mobx.js.org/refguide/object-api.html
 * @example
 * remove(thing, key)
 */
const remove = _mobx.remove;
/**
 * 如果集合中存在指定的 observable 属性就返回 true
 * @see https://cn.mobx.js.org/refguide/object-api.html
 * @example
 * has(thing, key)
 */
const has = _mobx.has;

/**
 * @example
 * import { get, set, observable, values } from "mobx@4"
 * 
 * const twitterUrls = observable.object({
 *     "John": "twitter.com/johnny"
 * })
 *
 * autorun(() => {
 *     console.log(get(twitterUrls, "Sara")) // get 可以追踪尚未存在的属性
 * })
 *
 * autorun(() => {
 *     console.log("All urls: " + values(twitterUrls).join(", "))
 * })
 *
 * set(twitterUrls, { "Sara" : "twitter.com/horsejs"})
 */

export default {
    autorun,
    reaction,
    when,
    action,
    comparer,
    runInAction,
    configure,
    computed,
    observable,
    values,
    keys,
    entries,
    set,
    get,
    remove,
    has
}